package com.msh.frame.common.util;

import com.msh.frame.client.exception.BizException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RsaUtil {

  private static final KeyFactory keyFactory;
  /**
   * 非对称密钥算法
   */
  public static final String KEY_ALGORITHM = "RSA";

  /**
   * 密钥长度，DH算法的默认密钥长度是1024 密钥长度必须是64的倍数，在512到65536位之间
   */
  private static final int KEY_SIZE = 512;

  static {
    try {
      keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    } catch (NoSuchAlgorithmException e) {
      throw new BizException("创建keyFactory 失败");
    }
  }

  public static RsaInstance getInstance() {
    return getInstance(KEY_SIZE);
  }

  public static RsaInstance getInstance(int keySize) {
    return new RsaInstance(keySize);
  }

  public static void main(String[] args)
      throws InterruptedException, BadPaddingException, IllegalBlockSizeException, IOException, InvalidKeySpecException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
    RsaInstance instance = RsaUtil.getInstance(512);
    MultiThreadTestUtil.threadTest(instance, e -> {
      String str = String.valueOf(new Random().nextDouble());
//      System.out.println(str);
      String s4 = e.encryptByPublic(str);
//      System.out.println(s4);
      String s5 = e.decryptByPrivate(s4);
//      System.out.println(s5);
      if (!str.equals(s5)) {
        System.out.println(str.equals(s5));
      }
    });
  }

  /**
   * 公钥加密
   */
  public static String encryptByPublicKey(byte[] bs, PublicKey publicKey)
      throws BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
    //数据解密
    Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    byte[] bytes = cipher.doFinal(bs);
    return Base64Util.encryptBuffer(bytes);
  }

  public static String encryptByPublicKey(String str, PublicKey publicKey)
      throws BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
    return encryptByPublicKey(str.getBytes(StandardCharsets.UTF_8), publicKey);
  }

  /**
   * 私钥加密
   */
  public static String encryptByPrivateKey(byte[] bs, PrivateKey privateKey)
      throws BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
    //数据解密
    Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, privateKey);
    byte[] bytes = cipher.doFinal(bs);
    return Base64Util.encryptBuffer(bytes);
  }

  public static String encryptByPrivateKey(String str, PrivateKey privateKey)
      throws IllegalBlockSizeException, UnsupportedEncodingException, NoSuchPaddingException, BadPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
    return encryptByPrivateKey(str.getBytes(StandardCharsets.UTF_8), privateKey);
  }

  /**
   * 公钥解密
   */
  public static String decryptByPublicKey(byte[] bs, PublicKey publicKey)
      throws BadPaddingException, IllegalBlockSizeException, IOException, InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
    Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
    cipher.init(Cipher.DECRYPT_MODE, publicKey);
    return new String(cipher.doFinal(bs), "UTF-8");
  }

  public static String decryptByPublicKey(String str, PublicKey publicKey)
      throws IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException, IOException, InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException {
    return decryptByPublicKey(Base64Util.decodeBuffer(str), publicKey);
  }

  /**
   * 私钥解密
   */
  public static String decryptByPrivateKey(byte[] bs, PrivateKey privateKey)
      throws BadPaddingException, IllegalBlockSizeException, IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
    //数据解密
    Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
    cipher.init(Cipher.DECRYPT_MODE, privateKey);
    return new String(cipher.doFinal(bs), "UTF-8");
  }

  public static String decryptByPrivateKey(String str, PrivateKey privateKey)
      throws IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException, IOException, NoSuchAlgorithmException, InvalidKeyException {
    return decryptByPrivateKey(Base64Util.decodeBuffer(str), privateKey);
  }

  /**
   * 通过公有key code  转换为公有密钥
   */
  public static PublicKey tranformPublicKey(String publicKeyCode) {
    //初始化公钥
    //密钥材料转换
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64Util.decodeBuffer(publicKeyCode));
    //产生公钥
    try {
      return keyFactory.generatePublic(x509KeySpec);
    } catch (InvalidKeySpecException e) {
      throw new BizException("转换PublicKey 失败");
    }
  }

  /**
   * 通过私有key code  转换为私有密钥
   */
  public static PrivateKey tranformPrivateKey(String privateKeyCode) {
    //取得私钥
    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(
        Base64Util.decodeBuffer(privateKeyCode));
    //生成私钥
    try {
      return keyFactory.generatePrivate(pkcs8KeySpec);
    } catch (InvalidKeySpecException e) {
      throw new BizException("转换PrivateKey 失败");
    }
  }

  @Data
  public static class RsaInstance {

    private RSAPublicKey rsaPublicKey;
    private RSAPrivateKey rsaPrivateKey;

    /**
     * 获取私有key code
     */
    public String getPrivateKeyCode() {
      return Base64Util.encryptBuffer(this.rsaPrivateKey.getEncoded());
    }

    /**
     * 获取公有key code
     */
    public String getPublicKeyCode() {
      return Base64Util.encryptBuffer(rsaPublicKey.getEncoded());
    }

    private RsaInstance(int keySize) {
      try {
        //实例化密钥生成器
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        //初始化密钥生成器
        keyPairGenerator.initialize(keySize);
        //生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        //甲方公钥
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        //甲方私钥
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        this.rsaPublicKey = rsaPublicKey;
        this.rsaPrivateKey = rsaPrivateKey;
      } catch (Exception e) {
        throw new BizException("生成实例失败");
      }

    }


    /**
     * 公钥加密
     */
    public String encryptByPublic(String str)
        throws BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
      return RsaUtil.encryptByPublicKey(str, rsaPublicKey);
    }

    public String encryptByPublic(byte[] bs)
        throws BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
      return RsaUtil.encryptByPublicKey(bs, rsaPublicKey);
    }

    /**
     * 私钥加密
     */
    public String encryptByPrivate(String str)
        throws BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
      return RsaUtil.encryptByPrivateKey(str, rsaPrivateKey);
    }

    public String encryptByPrivate(byte[] bs)
        throws BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
      return RsaUtil.encryptByPrivateKey(bs, rsaPrivateKey);
    }

    /**
     * 公钥解密
     */
    public String decryptByPublic(String str)
        throws BadPaddingException, IllegalBlockSizeException, IOException, InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
      return RsaUtil.decryptByPublicKey(str, rsaPublicKey);
    }

    public String decryptByPublic(byte[] bs)
        throws BadPaddingException, IllegalBlockSizeException, IOException, InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
      return RsaUtil.decryptByPublicKey(bs, rsaPublicKey);
    }

    /**
     * 私钥解密
     */
    public String decryptByPrivate(String str)
        throws BadPaddingException, IllegalBlockSizeException, IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
      return RsaUtil.decryptByPrivateKey(str, rsaPrivateKey);
    }

    public String decryptByPrivate(byte[] bs)
        throws BadPaddingException, IllegalBlockSizeException, IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
      return RsaUtil.decryptByPrivateKey(bs, rsaPrivateKey);
    }
  }
}
